package net.ge.mosq.service.impl;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import net.ge.mosq.jdo.entity.TransactionInfo;
import net.ge.mosq.jdo.factory.PMF;
import net.ge.mosq.service.AccountService;
import net.ge.mosq.service.ServiceFactory;

/**
 * @author noodles.wumu@gmail.com
 * @since 2012/07/02 16:25:21
 * @version 1.0
 */
public class TransactionService extends BaseService {

	public void create(TransactionInfo aTransaction) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Date currentDate = new Date();
			aTransaction.setCreateTime(currentDate);
			aTransaction.setUpdateTime(currentDate);
			pm.makePersistent(aTransaction);
		} finally {
			pm.close();
		}
	}

	public void delete(TransactionInfo aTransaction) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			pm.deletePersistent(aTransaction);
		} finally {
			pm.close();
		}
	}

	public void update(TransactionInfo aTransaction) throws IllegalAccessException, InvocationTargetException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			TransactionInfo tran = pm.getObjectById(TransactionInfo.class, aTransaction.getId());
			tran.setUpdateCount(tran.getUpdateCount() + 1);
			tran.setUpdateTime(new Date());
		} finally {
			pm.close();
		}
	}

	/**
	 * 
	 * @param accId
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<TransactionInfo> getTransactionInfoList(Long accId) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		List<TransactionInfo> transactionInfoList = new ArrayList<TransactionInfo>();
		List<TransactionInfo> plusResult = null;
		List<TransactionInfo> minusResult = null;
		Query plusAccQuery = null;
		Query minusAccQuery = null;
		try {
			pm.getDetachAllOnCommit();
			plusAccQuery = pm.newQuery(TransactionInfo.class);
			plusAccQuery.setFilter("plusAccId == accid");
			plusAccQuery.declareParameters("Long accid");
			plusResult = (List<TransactionInfo>) plusAccQuery.execute(accId);

			minusAccQuery = pm.newQuery(TransactionInfo.class);
			minusAccQuery.setFilter("minusAccId == accid");
			minusAccQuery.declareParameters("Long accid");
			minusResult = (List<TransactionInfo>) minusAccQuery.execute(accId);

			AccountService as = ServiceFactory.getAccountService();
			if (plusResult != null) {
				for (TransactionInfo transactionInfo : plusResult) {
					transactionInfoList.add(transactionInfo);
					transactionInfo.setTransAccId(transactionInfo.getMinusAccId());
					transactionInfo.setTransAccName(as.getAccountNameWithAllParentById(transactionInfo.getMinusAccId()));
				}
			}

			if (minusResult != null) {
				for (TransactionInfo transactionInfo : minusResult) {
					transactionInfoList.add(transactionInfo);
					transactionInfo.setTransAccId(transactionInfo.getPlusAccId());
					transactionInfo.setTransAccName(as.getAccountNameWithAllParentById(transactionInfo.getPlusAccId()));
				}
			}
		} finally {
			if (plusAccQuery != null)
				plusAccQuery.closeAll();
			if (minusAccQuery != null)
				minusAccQuery.closeAll();
			pm.close();
		}

		Collections.sort(transactionInfoList, new Comparator<TransactionInfo>() {
			public int compare(TransactionInfo o1, TransactionInfo o2) {
				int cop = o1.getTransDate().compareTo(o2.getTransDate());
				if (cop == 0) {
					if (o1.getNum() == null && o2.getNum() == null) return 0;
					if (o1.getNum() == null) return -1;
					if (o2.getNum() == null) return 1;
					return o1.getNum() - o2.getNum();
				}
				return cop;
			};
		});

		TransactionInfo transInfo = null;
		for (int i = 0; i < transactionInfoList.size(); i++) {
			transInfo = transactionInfoList.get(i);
			if (transInfo.getPlusAccId().equals(accId)) {
				transInfo.setTransAmountLabel1(transInfo.getTransAmount());
				if (i == 0) {
					transInfo.setBalance(transInfo.getTransAmount());
				} else {
					transInfo.setBalance(transactionInfoList.get(i - 1).getBalance().add(transInfo.getTransAmount()));
				}
			} else {
				transInfo.setTransAmountLabel2(transInfo.getTransAmount());
				if (i == 0) {
					transInfo.setBalance(transInfo.getTransAmount().negate());
				} else {
					transInfo.setBalance(transactionInfoList.get(i - 1).getBalance().subtract(transInfo.getTransAmount()));
				}
			}
		}
		return transactionInfoList;
	}
	
	@SuppressWarnings("unchecked")
	public BigDecimal getBalanceById(Long id) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query plusAccQuery = null;
		Query minusAccQuery = null;
		BigDecimal balance = BigDecimal.ZERO;
		try {
			pm.getDetachAllOnCommit();
			plusAccQuery = pm.newQuery(TransactionInfo.class);
			plusAccQuery.setFilter("plusAccId == accid");
			plusAccQuery.declareParameters("Long accid");
			List<TransactionInfo> plusResult = (List<TransactionInfo>) plusAccQuery.execute(id);

			minusAccQuery = pm.newQuery(TransactionInfo.class);
			minusAccQuery.setFilter("minusAccId == accid");
			minusAccQuery.declareParameters("Long accid");
			List<TransactionInfo> minusResult = (List<TransactionInfo>) minusAccQuery.execute(id);

			if (plusResult != null) {
				for (TransactionInfo transactionInfo : plusResult) {
					balance = balance.add(transactionInfo.getTransAmount());
				}
			}

			if (minusResult != null) {
				for (TransactionInfo transactionInfo : minusResult) {
					balance = balance.subtract(transactionInfo.getTransAmount());
				}
			}
		} finally {
			if (plusAccQuery != null)
				plusAccQuery.closeAll();
			if (minusAccQuery != null)
				minusAccQuery.closeAll();
			pm.close();
		}
		return balance;
	}
}
